#pragma once

#include"Common.h"

#include"SpendType.h"
#include"Resource.h"
#include"Supply.h"
#include"Tickable.h"

namespace Macro
{
	class SpendTypeData
	{
	public:
		SpendTypeData()
			:_amountOrLevel(0)
		{
		}
		inline void AddRealUnit(Unit* realUnit)
		{
			const SpendType& spendType(GetSpendType(realUnit->getType()));
			//--
			if(!realUnit->isCompleted() || NeedInstance(spendType))
			{
				_tickables.push_back(Tickable(realUnit));
			}
			if(realUnit->isCompleted())
			{
				++_amountOrLevel;
			}
		}
		inline void SetAmount(const int& newAmount)
		{
			_amountOrLevel = newAmount;
		}
		inline void IncreaseCompleteAmount(const int& amount)
		{
			_amountOrLevel += amount;
		}
		inline void PushBackIncoming(const SpendType& spendType)
		{
			_tickables.push_back(Tickable(spendType , FrameNeeded(spendType)));
		}
		inline void DecreaseCompleteAmount()
		{
			--_amountOrLevel;
		}
		inline int GetCompleteAmount() const
		{
			return _amountOrLevel;
		}
		inline int GetTotalAmount() const
		{
			return _amountOrLevel + _tickables.size();
		}
		inline bool StartSpendIfPossible(const SpendType& spendType)
		{
			for(list<Tickable>::iterator it = _tickables.begin() ; it != _tickables.end() ; ++it)
			{
				Tickable& whichTickable(*it);
				if(whichTickable.IsCompleted() && whichTickable.CanProduceNow())
				{
					whichTickable.StartProduce(spendType);
					return true;
				}
			}
			return false;
		}	
		inline list<Tickable>& GetList()
		{
			return _tickables;
		}
	private:
		int _amountOrLevel;
		list<Tickable> _tickables;
	};

	class PlayerState
	{
	public:
		PlayerState(Player* player)
			:_resource(player),
			_supply(player),
			_armySizeIncreased(false),
			_armyStrength(0.0f)
		{
			for each(Unit* whichUnit in player->getUnits())
			{
				UnitType unitType(whichUnit->getType());
				if(unitType != UnitTypes::Zerg_Larva)
				{
					if(!whichUnit->isCompleted())
					{
						unitType = whichUnit->getBuildType();
					}
					const SpendType& spendType(GetSpendType(unitType));
					if(spendType != Undefined)
					{
						_spendTypeInfos[spendType].AddRealUnit(whichUnit);
					}
				}
			}

			//tech
			for(int techIndex = 0 ; techIndex < TechnologySpendTypes.size() ; ++techIndex)
			{
				const SpendType& spendType(TechnologySpendTypes[techIndex]);
				const TechType& whichTech(GetTechType(spendType));
				if(player->hasResearched(whichTech))
				{
					_spendTypeInfos[spendType].SetAmount(1);
				}
			}

			//upgrade
			/*for()
			{
			}*/

			SetIncome();
			ComputeArmyStrength();
		}
		inline void GetPossibleNexts(list<SpendType>& possibleNexts)
		{
			for(int typeIndex = 0 ; typeIndex < SPEND_TYPE_AMOUNT ; ++typeIndex)
			{
				const SpendType whichSpendType = (SpendType)typeIndex;
				const vector<SpendType>& requires(GetRequires(whichSpendType));
				bool techAllow(true);
				for(int index = 0 ; index < requires.size() ; ++index)
				{
					if(_spendTypeInfos[requires[index]].GetTotalAmount() <= 0)
					{
						techAllow = false;
						break;
					}
				}
				if(techAllow && _spendTypeInfos[whichSpendType].GetTotalAmount() < AmountLimit(whichSpendType))
				{
					if(!_supply.AllowBuild(whichSpendType))
					{
						if(_spendTypeInfos[Overlord].GetList().size() * 16 + _supply.Remain() >= SupplyRequired(whichSpendType))
						{
							possibleNexts.push_back(whichSpendType);
						}
					}
					else
					{
						possibleNexts.push_back(whichSpendType);
					}
				}
			}
		}
		inline void SetNext(const SpendType& next)
		{
			int spendAmount(0);
			if(IsArmy(next))
			{
				const int mineralCost(MineralPrice(next));
				const float mineralIncome(_resource.MineralIncome());
				spendAmount = (int)(mineralIncome * 200.0f / (float)mineralCost);
			}
			else
			{
				spendAmount = 1;
			}

			for(int whichPushBack = 0 ; whichPushBack < spendAmount ; ++whichPushBack)
			{
				_nextSpendList.push_back(next);
			}
		}
		inline bool NextProductionNotSetted() const
		{
			return _nextSpendList.empty();
		}
		inline void Tick()
		{
			_resource.Tick();

			_armySizeIncreased = false;
			for(int spendIndex = 0 ; spendIndex < SPEND_TYPE_AMOUNT ; ++spendIndex)
			{
				SpendType spendType = (SpendType)spendIndex;
				SpendTypeData& entry(_spendTypeInfos[spendType]);
				list<Tickable>& tickables(entry.GetList());
				for(list<Tickable>::iterator it = tickables.begin() ; it != tickables.end() ; )
				{
					Tickable& whichTickable(*it);
					whichTickable.Tick(spendType == Lair);
					if(whichTickable.MorphJustFinished())
					{
						_supply.SpendFinished(spendType);
						entry.IncreaseCompleteAmount(BornAmount(spendType));

						const SpendTypeCategory& spendCategory(GetCategory(spendType));
						if(spendCategory == CreateUnit)
						{
							NewUnitJoinArmy(spendType);
						}
						if(spendType == Drone || spendType == Extractor)
						{
							SetIncome();
						}

						if(!NeedInstance(spendType))
						{
							tickables.erase(it++);
						}
						else
						{
							++it;
						}
					}
					else
					{
						if(whichTickable.IsCompleted())
						{
							if(whichTickable.ProductionJustFinished())
							{
								const SpendType& productionSpendType(whichTickable.ProduceWhat());
								_supply.SpendFinished(productionSpendType);
								_spendTypeInfos[productionSpendType].IncreaseCompleteAmount(BornAmount(productionSpendType));
							}
						}
						else // not finish
						{
						}
						++it;
					}
				}
			}

			//NEXT
			SpendType next(*_nextSpendList.begin());
			if(_resource.AllowSpend(next) && _supply.AllowBuild(next) && TechnologyAllowNextSpendNow())
			{
				bool success(false);
				if(GetSpendMethod(next) == Morph)
				{
					const SpendType& originalType(OriginalSpendType(next));

					if(originalType == Larva)
					{
						list<Tickable>& hatcheries(_spendTypeInfos[Hatchery].GetList());
						for(list<Tickable>::iterator it = hatcheries.begin() ; it != hatcheries.end() ; ++it)
						{
							Tickable& whichHatchery(*it);
							if(whichHatchery.IsCompleted() && whichHatchery.HaveLarva())
							{
								whichHatchery.RemoveOneLarva();
								_spendTypeInfos[next].PushBackIncoming(next);
								success = true;
								goto CHECK_SUCCESS;
							}
						}

						list<Tickable>& lairs(_spendTypeInfos[Lair].GetList());
						for(list<Tickable>::iterator it = lairs.begin() ; it != lairs.end() ; ++it)
						{
							Tickable& whichLair(*it);
							if(whichLair.HaveLarva())
							{
								whichLair.RemoveOneLarva();
								_spendTypeInfos[next].PushBackIncoming(next);
								success = true;
								goto CHECK_SUCCESS;
							}
						}
					}
					else
					{
						if(_spendTypeInfos[originalType].GetCompleteAmount() > 0)
						{
							_spendTypeInfos[originalType].DecreaseCompleteAmount();
							_spendTypeInfos[next].PushBackIncoming(next);
							success = true;
							goto CHECK_SUCCESS;
						}
					}
				}
				else if(GetSpendMethod(next) == Make)
				{
					const SpendType& maker(WhatMakes(next));
					if(_spendTypeInfos[maker].StartSpendIfPossible(next))
					{
						success = true;
						goto CHECK_SUCCESS;
					}
				}
CHECK_SUCCESS:;
				if(success)
				{
					_supply.SpendStarted(next);
					_resource.Spend(next);
					_nextSpendList.pop_front();
				}
			}
		}
		inline bool ArmySizeIncreasedInThisFrame() const
		{
			return _armySizeIncreased;
		}
		inline float SimulatedFight(const PlayerState& anotherPlayerState) const
		{
			return _armyStrength - anotherPlayerState._armyStrength;
		}
	private:
		inline void SetIncome()
		{
			const int amountOfDrones(_spendTypeInfos[Drone].GetCompleteAmount());
			const int amountOfExtractor(_spendTypeInfos[Extractor].GetCompleteAmount());
			if(amountOfExtractor > 0)
			{
				float amountOfGasingDrones = amountOfExtractor * 3;
				if(amountOfGasingDrones > amountOfDrones)
				{
					amountOfGasingDrones = amountOfDrones;
				}
				_resource.SetAmountOfHarvestingWorkers(amountOfDrones - amountOfGasingDrones , amountOfGasingDrones);
			}
			else
			{
				_resource.SetAmountOfHarvestingWorkers(amountOfDrones , 0);
			}
		}
		inline void ComputeArmyStrength()
		{
			_armyStrength = 0.0f;
			_armyStrength += (float)_spendTypeInfos[Zergling].GetCompleteAmount() * 25.0f;
			_armyStrength += (float)_spendTypeInfos[Hydralisk].GetCompleteAmount() * 150.0f;
			_armyStrength += (float)_spendTypeInfos[Lurker].GetCompleteAmount() * 400.0f;
		}
		inline void NewUnitJoinArmy(const SpendType& spendType)
		{
			if(spendType == Zergling)
			{
				_armyStrength += 50.0f;
				_armySizeIncreased = true;
			}
			else if(spendType == Hydralisk)
			{
				_armyStrength += 150.0f;
				_armySizeIncreased = true;
			}
			else if(spendType == Lurker)
			{
				_armyStrength += 400.0f;
				_armySizeIncreased = true;
			}
		}
		inline bool TechnologyAllowNextSpendNow() const
		{
			const vector<SpendType>& requires(GetRequires(*_nextSpendList.begin()));
			for(int index = 0 ; index < requires.size() ; ++index)
			{
				if(_spendTypeInfos[requires[index]].GetCompleteAmount() <= 0)
				{
					return false;
				}
			}
			return true;
		}
		//--
		Resource _resource;
		Supply _supply;
		SpendTypeData _spendTypeInfos[SPEND_TYPE_AMOUNT];

		list<SpendType> _nextSpendList;

		bool _armySizeIncreased;
		float _armyStrength;
	};
}